package vn.edu.saigontech.ncms.util;

import java.sql.Time;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import vn.edu.saigontech.ncms.model.user.LoggingRecord;
import vn.edu.saigontech.ncms.service.LoggingRecordService;

public class DateUtil {
	
	public static final String DEFAULT_PATTERN = "dd/MM/yyyy"; 
	private static final String VNMON = "Thứ Hai";
	private static final String VNTUE = "Thứ Ba";
	private static final String VNWED = "Thứ Tư";
	private static final String VNTHU = "Thứ Năm";
	private static final String VNFRI = "Thứ Sáu";
	private static final String VNSAT = "Thứ Bảy";
	private static final String VNSUN = "Chủ Nhật";

	public static Time newTime() {
		return new Time(new Date().getTime());
	}

	public static Time newTime(int hour, int minute) {
		return new Time(newDate(0, 0, 0, hour, minute, 0).getTime());
	}

	public static Time newTime(int hour, int minute, int second) {
		return new Time(newDate(0, 0, 0, hour, minute, second).getTime());
	}

	public static Time toTime(Calendar calendar) {
		return new Time(calendar.getTimeInMillis());
	}
	
	public static String toString(Time time, String pattern) {
		SimpleDateFormat formatter = new SimpleDateFormat(pattern);
		return formatter.format(time);
	}

	public static Time timeFromString(String time, String pattern) {
		SimpleDateFormat formatter = new SimpleDateFormat(pattern);
		try {
			return new Time(formatter.parse(time).getTime());
		} catch (Exception ex) {
		}
		return null;
	}

	public static Date newDate() {
		return new Date();
	}

	public static Date newDate(int day, int month, int year) {
		return newDate(day, month, year, 0, 0, 0);
	}

	public static Date newDate(int day, int month, int year, int hour,
			int minute) {
		return newDate(day, month, year, hour, minute, 0);
	}

	public static Date newDate(int day, int month, int year, int hour,
			int minute, int second) {
		Calendar calendar = new GregorianCalendar(year, month - 1, day, hour,
				minute, second);
		return toDate(calendar);
	}

	public static Date addDay(Date date, int numOfDays) {
		return addHour(date, numOfDays * 24);
	}

	public static Date addHour(Date date, int numOfHours) {
		return addMinute(date, numOfHours * 60);
	}

	public static Date addMinute(Date date, int numOfMinutes) {
		Calendar newDate = toCalendar(date);
		newDate.add(Calendar.MINUTE, numOfMinutes);
		return toDate(newDate);
	}

	public static Date startOfDate(Date date) {
		Calendar start = toCalendar(date);
		start.set(Calendar.HOUR_OF_DAY, start.getMinimum(Calendar.HOUR_OF_DAY));
		start.set(Calendar.MINUTE, start.getMinimum(Calendar.MINUTE));
		start.set(Calendar.SECOND, start.getMinimum(Calendar.SECOND));
		return toDate(start);
	}

	public static Date endOfDate(Date date) {
		Calendar end = toCalendar(date);
		end.set(Calendar.HOUR_OF_DAY, end.getMaximum(Calendar.HOUR_OF_DAY));
		end.set(Calendar.MINUTE, end.getMaximum(Calendar.MINUTE));
		end.set(Calendar.SECOND, end.getMaximum(Calendar.SECOND));
		return toDate(end);
	}

	public static Date startOfWeek(Date date) {
		Calendar calendar = toCalendar(date);
		calendar.set(Calendar.DAY_OF_WEEK, calendar
				.getMinimum(Calendar.DAY_OF_WEEK));
		return startOfDate(toDate(calendar));
	}

	public static Date endOfWeek(Date date) {
		Calendar calendar = toCalendar(date);
		calendar.set(Calendar.DAY_OF_WEEK, calendar
				.getActualMaximum(Calendar.DAY_OF_WEEK));
		return endOfDate(toDate(calendar));
	}

	public static Date startOfMonth(Date date) {
		Calendar calendar = toCalendar(date);
		calendar.set(Calendar.DAY_OF_MONTH, calendar
				.getActualMinimum(Calendar.DAY_OF_MONTH));
		return startOfDate(toDate(calendar));
	}

	public static Date endOfMonth(Date date) {
		Calendar calendar = toCalendar(DateUtil.newDate());
		calendar.set(Calendar.DAY_OF_MONTH, calendar
				.getActualMaximum(Calendar.DAY_OF_MONTH));
		return startOfDate(toDate(calendar));
	}

	public static boolean inSameDay(Date date1, Date date2) {
		return !(date1.before(startOfDate(date2)) || date1
				.after(endOfDate(date2)));
	}

	public static boolean inSameMonth(Date date1, Date date2) {
		Calendar cal1 = toCalendar(date1);
		Calendar cal2 = toCalendar(date2);
		return cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH)
				&& cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR);
	}

	public static boolean inSameWeek(Date date1, Date date2) {
		Calendar cal1 = toCalendar(date1);
		Calendar cal2 = toCalendar(date2);
		return inSameMonth(date1, date2)
				&& cal1.get(Calendar.WEEK_OF_YEAR) == cal2
						.get(Calendar.WEEK_OF_YEAR);
	}

	public static boolean isBetween(Date date1, Date date2, Date date3) {
		return !(date1.before(date2) || date1.after(date3));
	}

	public static Calendar toCalendar(Date date) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		calendar.setFirstDayOfWeek(Calendar.MONDAY);
		return calendar;
	}

	public static Date toDate(Calendar calendar) {
		return calendar.getTime();
	}

	public static String toString(Date date, String pattern) {
		SimpleDateFormat formatter = new SimpleDateFormat(pattern);
		try {
			return formatter.format(date);
		} catch (Exception ex) {
		}
		return "";
	}
	
	public static String toString(Date date) {
		SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
		try {
			return formatter.format(date);
		} catch (Exception ex) {
		}
		return "";
	}
	
	public static Date fromStringWithTime(String date, String pattern) {
		SimpleDateFormat formatter = new SimpleDateFormat(pattern);
		try {
			Date now = DateUtil.newTime();
			String time = toString(now, "HH:mm:ss");
			return formatter.parse(date + " " + time);
		} catch (Exception ex) {
		}
		return null;
	}

	public static Date fromString(String date, String pattern) {
		SimpleDateFormat formatter = new SimpleDateFormat(pattern);
		try {
			return formatter.parse(date);
		} catch (Exception ex) {
		}
		return null;
	}

	public static Collection<Date> sequence(Date from, Date to) {
		Set<Date> dates = new TreeSet<Date>();
		if (from == null && to != null) {
			dates.add(to);
		}
		if (from != null && to == null) {
			dates.add(from);
		}
		if (dates.isEmpty()) {
			Calendar tempDate = toCalendar(from);
			while (tempDate.before(toCalendar(to))
					|| tempDate.equals(toCalendar(to))) {
				dates.add(toDate(tempDate));
				tempDate.add(Calendar.DAY_OF_MONTH, 1);
			}
		}
		return dates;
	}

	public static List<Date> sequenceAsList(Date from, Date to) {
		List<Date> dates = new ArrayList<Date>();
		if (from == null && to != null) {
			dates.add(to);
		}
		if (from != null && to == null) {
			dates.add(from);
		}
		if (dates.isEmpty()) {
			Calendar tempDate = toCalendar(from);
			while (tempDate.before(toCalendar(to))
					|| tempDate.equals(toCalendar(to))) {
				dates.add(toDate(tempDate));
				tempDate.add(Calendar.DAY_OF_MONTH, 1);
			}
		}
		return dates;
	}

	public static int getDayOfWeek(Date date) {
		return toCalendar(date).get(Calendar.DAY_OF_WEEK);
	}

	public static int getMonth(Date date) {
		return toCalendar(date).get(Calendar.MONTH);
	}

	public static int getYear(Date date) {
		return toCalendar(date).get(Calendar.YEAR);
	}

	public static int getDayOfMonth(Date date) {
		return toCalendar(date).get(Calendar.DAY_OF_MONTH);
	}

	public static int getHour(Date date) {
		return toCalendar(date).get(Calendar.HOUR_OF_DAY);
	}

	public static int getMinute(Date date) {
		return toCalendar(date).get(Calendar.MINUTE);
	}

	public static int getWeekOfYear(Date date) {
		Calendar cal = toCalendar(date);
		return cal.get(Calendar.WEEK_OF_YEAR);
	}

	public static Date getStartDateOfMonth(int month, int year) {
		return newDate(1, month, year);
	}

	public static Date getEndDateOfMonth(int month, int year) {
		return newDate(toCalendar(getStartDateOfMonth(month, year)).getActualMaximum(
				Calendar.DAY_OF_MONTH), month, year);
	}

	public static Date getDateOfThisWeek(int dayOfWeek) {
		Calendar cal = DateUtil.toCalendar(DateUtil.newDate());
		cal
				.add(Calendar.DAY_OF_MONTH,
						(((dayOfWeek == Calendar.SUNDAY) ? (dayOfWeek + 7)
								: dayOfWeek) - cal.get(Calendar.DAY_OF_WEEK)));
		return DateUtil.toDate(cal);
	}

	public static Date getDateOfThisWeek(Date date, int dayOfWeek) {
		Calendar cal = DateUtil.toCalendar(date);
		cal
				.add(Calendar.DAY_OF_MONTH,
						(((dayOfWeek == Calendar.SUNDAY) ? (dayOfWeek + 7)
								: dayOfWeek) - cal.get(Calendar.DAY_OF_WEEK)));
		return DateUtil.toDate(cal);
	}

	// CUONGPC
	public static Date get26OfThisMonth(Date date) {
		Calendar calendar = toCalendar(DateUtil.newDate());
		calendar.set(Calendar.DAY_OF_MONTH, 26);
		return startOfDate(toDate(calendar));
	}

	public static Date get26OfLastMonth(Date date) {
		Calendar calendar = toCalendar(DateUtil.newDate());
		calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) - 1);
		calendar.set(Calendar.DAY_OF_MONTH, 26);
		return startOfDate(toDate(calendar));
	}
	
	public static String dateOfWeekVN(Date date) {
		Calendar cal = DateUtil.toCalendar(date);
		int value = cal.get(Calendar.DAY_OF_WEEK);
		switch (value) {
			case Calendar.MONDAY:
				return VNMON;
			case Calendar.TUESDAY:
				return VNTUE;
			case Calendar.WEDNESDAY:
				return VNWED;
			case Calendar.THURSDAY:
				return VNTHU;
			case Calendar.FRIDAY:
				return VNFRI;
			case Calendar.SATURDAY:
				return VNSAT;
			case Calendar.SUNDAY:
				return VNSUN;
		}
		return "";
	}

	public static String getDateOfWeekAsString(Date date) {
		Calendar cal = DateUtil.toCalendar(date);
		int value = cal.get(Calendar.DAY_OF_WEEK);
		switch (value) {
		case Calendar.MONDAY:
			return "Mon";
		case Calendar.TUESDAY:
			return "Tue";
		case Calendar.WEDNESDAY:
			return "Wed";
		case Calendar.THURSDAY:
			return "Thu";
		case Calendar.FRIDAY:
			return "Fri";
		case Calendar.SATURDAY:
			return "Sat";
		case Calendar.SUNDAY:
			return "Sun";
		}
		return "";
	}

	public static String getDateOfWeekAsString(Integer value) {
		switch (value) {
		case Calendar.MONDAY:
			return "Mon";
		case Calendar.TUESDAY:
			return "Tue";
		case Calendar.WEDNESDAY:
			return "Wed";
		case Calendar.THURSDAY:
			return "Thu";
		case Calendar.FRIDAY:
			return "Fri";
		case Calendar.SATURDAY:
			return "Sat";
		case Calendar.SUNDAY:
			return "Sun";
		}
		return "";
	}
	
	public static String getDateOfWeekAsChar(Integer value) {
		switch (value) {
		case Calendar.MONDAY:
			return "M";
		case Calendar.TUESDAY:
			return "T";
		case Calendar.WEDNESDAY:
			return "W";
		case Calendar.THURSDAY:
			return "T";
		case Calendar.FRIDAY:
			return "F";
		case Calendar.SATURDAY:
			return "S";
		case Calendar.SUNDAY:
			return "S";
		}
		return "";
	}

	public static String getMonthAsString(Date date) {
		int value = DateUtil.getMonth(date);
		switch (value) {
		case Calendar.JANUARY:
			return "Jan";
		case Calendar.FEBRUARY:
			return "Feb";
		case Calendar.MARCH:
			return "Mar";
		case Calendar.APRIL:
			return "Apr";
		case Calendar.MAY:
			return "May";
		case Calendar.JUNE:
			return "Jun";
		case Calendar.JULY:
			return "Jul";
		case Calendar.AUGUST:
			return "Aug";
		case Calendar.SEPTEMBER:
			return "Sep";
		case Calendar.OCTOBER:
			return "Oct";
		case Calendar.NOVEMBER:
			return "Nov";
		case Calendar.DECEMBER:
			return "Dec";
		}
		return "";
	}
	
	public static String getMonthAsFullString(Date date) {
		int value = DateUtil.getMonth(date);
		switch (value) {
		case Calendar.JANUARY:
			return "January";
		case Calendar.FEBRUARY:
			return "February";
		case Calendar.MARCH:
			return "March";
		case Calendar.APRIL:
			return "April";
		case Calendar.MAY:
			return "May";
		case Calendar.JUNE:
			return "June";
		case Calendar.JULY:
			return "July";
		case Calendar.AUGUST:
			return "August";
		case Calendar.SEPTEMBER:
			return "September";
		case Calendar.OCTOBER:
			return "Octocber";
		case Calendar.NOVEMBER:
			return "November";
		case Calendar.DECEMBER:
			return "December";
		}
		return "";
	}
	
	public static boolean contains(List<Date> range, Date date, boolean yearlyRepeat) {
		if(yearlyRepeat) {
			Calendar target = DateUtil.toCalendar(date);
			Calendar firstDate = DateUtil.toCalendar(range.get(0));
			target.set(Calendar.YEAR, firstDate.get(Calendar.YEAR));
			date = DateUtil.toDate(target);
		}
		return range.contains(date);
	}
	
	public static int getField(Date date, int field) {
		return toCalendar(date).get(field);
	}
	
	public static long getDifferenceDate(Date endDate, Date startDate) {
		return Math.abs(endDate.getTime() - startDate.getTime()) / (24 * 60 * 60 * 1000);
	}
	
	/* halnt */
	public static Date backInMonth(Date date, int numOfMonths) {
		Calendar newDate = toCalendar(date);
		newDate.add(Calendar.MONTH, -numOfMonths);
		return toDate(newDate);
	}
	
	public static Date backToPreviousMonth(Date date) {
		return backInMonth(date, 1);
	}	
	
	public static void main(String[] args) {
		LoggingRecord record = LoggingRecordService.INSTANCE
				.findById((long) 89423);
		String ret = DateUtil.toString(record.getInteractedDate(),
				"MM/dd/yyyy hh:mm:ss");
		System.out.println(ret);
	}
	
	public static int diffMonth(int year1, int month1, int year2, int month2) {
		int nMonthsInAYear = 12; 
		return (year2 - year1) * nMonthsInAYear + (month2 - month1) + 1;
	}
	
	public static Date addMonth(Date date, int numOfMonths) {
		Calendar c = Calendar.getInstance();
		c.setTime(date);
		c.add(Calendar.MONTH, numOfMonths);
		return c.getTime();
	}
}